package es.uned.etsii.si3.taller;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import es.uned.etsii.si3.taller.misc.FuncionControlable;

public class FactoriaFunciones {	
    static
    public class DescripcionFuncion implements Comparable<DescripcionFuncion> {
    	private String identificador;
    	private String descripcion;
    	private boolean activa;
    	
    	public DescripcionFuncion(Funcion fn) {
    		setIdentificador(fn.getIdentificador());
    		setDescripcion(fn.getNombre());
    		setActiva(fn.getActivo());
    	}
    	
    	void setIdentificador(String identificador) {
    		this.identificador = identificador;
    	}
    	
    	void setDescripcion(String descripcion) {
    		this.descripcion = descripcion;
    	}
    	
    	void setActiva(boolean activa) {
    		this.activa = activa;
    	}
    	
    	public String getIdentificador() {
    		return identificador;
    	}
    	
    	public String getDescripcion() {
    		return descripcion;
    	}
    	
    	public boolean getActiva() {
    		return activa;
    	}
    	
    	@Override
    	public String toString() {
    		return descripcion;
    	}

    	@Override
    	public boolean equals(Object obj) {
    		if (this == obj)
    			return true;

    		if (!(obj instanceof DescripcionFuncion))
    			return false;

    		DescripcionFuncion otro = (DescripcionFuncion)obj;

    		return otro.getIdentificador().equals(identificador);
    	}
    	
    	@Override
    	public int hashCode() {
    		return identificador.hashCode();
    	}

    	public int compareTo(DescripcionFuncion otro) {
    		return identificador.compareTo(otro.getIdentificador());
    	}
    }
    
	// Constantes para referirse a las funciones
    
    // Funcionalidad del jefe de sección
    @FuncionControlable(descripcion = "Jefe Sección: Recepcionar vehículo")
    static public final String JEFE_SECCION_RECEPCIONAR_VEHICULO =                        "js_recepcionar_vehiculo";
    @FuncionControlable(descripcion = "Jefe Sección: Listar vehículos por recepcionar")
    static public final String JEFE_SECCION_LISTAR_V_RECEPCIONABLES =                     "js_listar_v_recepcionables";
    @FuncionControlable(descripcion = "Jefe Sección: Mostrar secciones pendientes a un vehículo")
    static public final String JEFE_SECCION_LISTAR_SECCIONES_PENDIENTES_V =               "js_listar_secciones_pendientes_v";

    @FuncionControlable(descripcion = "Jefe Sección: Mostrar pendiente de visto bueno")
    static public final String JEFE_SECCION_LISTAR_PENDIENTE_VB =                         "js_listar_pendientes_vb";
    @FuncionControlable(descripcion = "Jefe Sección: Ver detalles de pendiente de visto bueno")
    static public final String JEFE_SECCION_DETALLES_PENDIENTE_VB =                       "js_detalle_pendientes_vb";
    @FuncionControlable(descripcion = "Jefe Sección: Dar aceptar un trabajo pendiente de VB")
    static public final String JEFE_SECCION_ACEPTAR_PENDIENTE_VB =                        "js_aceptar_pendientes_vb";
    @FuncionControlable(descripcion = "Jefe Sección: Rechazar un trabajo pendiente de VB")
    static public final String JEFE_SECCION_RECHAZAR_PENDIENTE_VB =                       "js_rechazar_pendientes_vb";

    @FuncionControlable(descripcion = "Jefe Sección: Listar reparaciones recepcionadas")
    static public final String JEFE_SECCION_LISTAR_REP_RECEPCIONADAS =                    "js_listar_rep_recep";
    @FuncionControlable(descripcion = "Jefe Sección: Listar trabajos de reparación recepcionada")
    static public final String JEFE_SECCION_LISTAR_TRABAJOS_REP_RECEPCIONADA =            "js_listar_trabajos_rep";
    @FuncionControlable(descripcion = "Jefe Sección: Listar mecánicos para asignar a un trabajo")
    static public final String JEFE_SECCION_LISTAR_MECANICOS =                            "js_listar_mecanicos";
    @FuncionControlable(descripcion = "Jefe Sección: Crear nueva tarea para una reparación")
    static public final String JEFE_SECCION_CREAR_TAREA =                                 "js_crear_tarea";
    @FuncionControlable(descripcion = "Jefe Sección: Modificar una tarea de una reparación")
    static public final String JEFE_SECCION_MODIFICAR_TAREA =                             "js_modificar_tarea";

    // Funcionalidad del jefe de taller
    @FuncionControlable(descripcion = "Jefe Taller: Nuevo Parte de entrada")
    static public final String JEFE_TALLER_NUEVO_PARTE_ENTRADA=                    "jt_nuevo_parte_entrada";
    @FuncionControlable(descripcion = "Jefe Taller: Partes sin presupuestar")
    static public final String JEFE_TALLER_PARTES_SIN_PRESUPUESTAR=                    "jt_partes_sin_presupuestar";
    @FuncionControlable(descripcion = "Jefe Taller: Presupuestos sin confirmar")
    static public final String JEFE_TALLER_PRESUPUESTOS_SIN_CONFIRMAR=                    "jt_presupuestos_sin_confirmar";
    @FuncionControlable(descripcion = "Jefe Taller: Albarán de custodia")
    static public final String JEFE_TALLER_ALBARAN_DE_CUSTODIA =                    "jt_albaran_custodia";
    @FuncionControlable(descripcion = "Jefe Taller: Planificar reparación")
    static public final String JEFE_TALLER_PLANIFICAR_REPARACION =                    "jt_planificar_reparacion";
    @FuncionControlable(descripcion = "Jefe Taller: Ver planificaciones")
    static public final String JEFE_TALLER_VER_PLANIFICACIONES =                    "jt_ver_planificaciones";
    @FuncionControlable(descripcion = "Jefe Taller: Pendiente Visto Bueno")
    static public final String JEFE_TALLER_PENDIENTE_VB =                    "jt_pte_vb";
    @FuncionControlable(descripcion = "Jefe Taller: Pendiente Aviso")
    static public final String JEFE_TALLER_PENDIENTE_AVISO =                    "jt_pte_aviso";
    @FuncionControlable(descripcion = "Jefe Taller: Facturar trabajo")
    static public final String JEFE_TALLER_FACTURAR_TRABAJO =                    "jt_facturar";

    // Funcionalidad del mecánico
    @FuncionControlable(descripcion = "Mecánicos: Gestionar trabajo")
    static public final String MECANICO_GESTIONAR_TRABAJO =                               "m_gestionar_trabajo";
    @FuncionControlable(descripcion = "Mecánicos: Recepcionar trabajo")
    static public final String MECANICO_RECEPCIONAR_TRABAJO =                             "m_recepcionar_trabajo";

    // Funcionalidad del usuario
    @FuncionControlable(descripcion = "Usuario: búsqueda de talleres")
    static public final String USUARIO_BUSCAR_TALLERES =                                  "usr_buscar_talleres";
    @FuncionControlable(descripcion = "Usuario: vehículos en taller")
    static public final String USUARIO_VEHICULOS_EN_TALLER =                              "usr_vehiculos_taller";
    @FuncionControlable(descripcion = "Usuario: presupuestos pendientes")
    static public final String USUARIO_PRESUPUESTOS_PENDIENTES =                          "usr_presupuestos";


    // Funcionalidad del administrador
    @FuncionControlable(descripcion = "Administrador: Ver carga de trabajo")
    static public final String ADMIN_VER_CARGA_DE_TRABAJO =                               "a_carga_trabajo";
    @FuncionControlable(descripcion = "Administrador: Dar de alta nuevos usuarios")
    static public final String ADMIN_NUEVO_USUARIO =                                      "a_nuevo_usuario";
    @FuncionControlable(descripcion = "Administrador: Dar de baja a usuarios")
    static public final String ADMIN_BAJA_USUARIO =                                       "a_baja_usuario";
    @FuncionControlable(descripcion = "Administrador: Modificar datos de usuarios")
    static public final String ADMIN_MODIFICAR_USUARIO =                                  "a_cambia_usuario";
    @FuncionControlable(descripcion = "Administrador: Dar de alta nuevos clientes")
    static public final String ADMIN_NUEVO_CLIENTE =                                      "a_nuevo_cliente";
    @FuncionControlable(descripcion = "Administrador: Dar de baja a clientes")
    static public final String ADMIN_BAJA_CLIENTE =                                       "a_baja_cliente";
    @FuncionControlable(descripcion = "Administrador: Modificar datos de clientes")
    static public final String ADMIN_MODIFICAR_CLIENTE =                                  "a_cambia_cliente";
    @FuncionControlable(descripcion = "Administrador: Dar de alta nuevos empleados")
    static public final String ADMIN_NUEVO_EMPLEADO =                                      "a_nuevo_empleado";
    @FuncionControlable(descripcion = "Administrador: Dar de baja a empleados")
    static public final String ADMIN_BAJA_EMPLEADO =                                       "a_baja_empleado";
    @FuncionControlable(descripcion = "Administrador: Modificar datos de empleados")
    static public final String ADMIN_MODIFICAR_EMPLEADO =                                  "a_cambia_empleado";
    
    static
    public class FuncionDesconocida extends Exception {
		private static final long serialVersionUID = 1L;    	
		public FuncionDesconocida(String message) {
			super(message);
		}
    }

	static
	private Map<String, Funcion> registro;

	/**
	 * Esta función debería ser llamada sólo una vez, al poblar
	 * la base de datos cuando arranca la aplicación.
	 * 
	 * Su misión es crear un registro interno de funciones, con sus
	 * respectivos estados de activación (para actuar como caché de
	 * la base de datos. Las funciones se rescatan de la base de datos
	 * usando los "atajos" proporcionados por las constantes descritas
	 * al principio de la clase. En caso de no existir, se crean al
	 * vuelo.
	 * 
	 * @throws SQLException
	 */
	static
	public void inicializar() throws SQLException {
		if (registro != null)
			return;
		
		registro = new HashMap<String, Funcion>();
		
		// Recorre la lista de campos de esta clase en busca de
		// los que están anotados con "FuncionControlable"
		
		for (Field campo: FactoriaFunciones.class.getFields()) {
			try {
				String atajo = (String) campo.get(String.class);

				FuncionControlable anotacion = campo.getAnnotation(FuncionControlable.class);
				if (anotacion == null)
					continue;

				Funcion funcion = Funcion.buscarPorIdentificador(atajo);
				if (funcion == null) {
					funcion = Funcion.crear(atajo, anotacion.descripcion());
				}
				registro.put(atajo, funcion);
			} catch (IllegalArgumentException e) {
				// No va a suceder...
			} catch (IllegalAccessException e) {
				// No va a suceder...
			}
		}
	}
	
	/**
	 * Establece el estado de activación de una función 
	 *
	 * @param funcion constante que se refiere a la función
	 * @param status  true (activar) o false (desactivar)
	 * 
	 * @throws FuncionDesconocida
	 * @throws SQLException
	 */
	
	static
	public void setEstaActiva(String funcion, boolean status) throws FuncionDesconocida, SQLException {
		if (!registro.containsKey(funcion))
			throw new FuncionDesconocida("La función '" + funcion + "' no se encuentra en el registro");
		
		if (status)
			registro.get(funcion).activar();
		else
			registro.get(funcion).desactivar();
	}
	
	/**
	 * Devuelve el estado de activación de una función
	 * 
	 * @param funcion constante que se refiere a la función
	 * 
	 * @return true si la función está activa o false en caso contrario
	 * @throws FuncionDesconocida
	 */
	static
	public boolean getEstaActiva(String funcion) throws FuncionDesconocida {
		if (!registro.containsKey(funcion))
			throw new FuncionDesconocida("La función '" + funcion + "' no se encuentra en el registro");
		
		return registro.get(funcion).getActivo();
	}
	
	static
	public Collection<DescripcionFuncion> getListaFunciones() throws SQLException {
		ArrayList<DescripcionFuncion> lista = new ArrayList<FactoriaFunciones.DescripcionFuncion>();
		
		for (Funcion funcion: registro.values()) {
			lista.add(new DescripcionFuncion(funcion));
		}
		
		Collections.sort(lista);
		
		return lista;
	}
}